package cn.tom.config;

import cn.tom.tool.JwtUtil;
import cn.tom.tool.XResp;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 登录验证， 其实还是用了 用户名密码验证方式。
 * 如果用户名和密码正确，
 * 那么过滤器将创建一个JWT Token 并在HTTP Response 的header中返回它，格式：token: "Bearer +具体token值"
 * JwtAuthenticationFilter.java
 */
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

   // private ThreadLocal<Boolean> rememberMe = new ThreadLocal<>();
    private AuthenticationManager authenticationManager;

    public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
        // 设置登录请求的 URL,   http.loginProcessingUrl("/login")
        super.setFilterProcessesUrl("/api/login");
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response) throws AuthenticationException {
        // 获取网页输入的账号及密码
        String pwd = obtainPassword(request);
        String usr = obtainUsername(request);

        System.out.println("attemptAuthentication usr=" + usr + "    pwd=" + pwd);
        // 只不过由于这个方法源码的是把用户名和密码这些参数的名字是死的，所以重写了一下
        UsernamePasswordAuthenticationToken authRequest =
                new UsernamePasswordAuthenticationToken(usr, pwd);
        //如下这一行就会找 UserDetailsService.loadUserByUsername( usr ) 去主动校验密码是否一致
        return authenticationManager.authenticate(authRequest);


    }
    /**
     * 如果验证成功，就生成token并返回
     * successful  成功    Authentication 你是谁， 登录验证
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain,
                                            Authentication authentication)  {
        System.out.println("登录成功 ....");
        UserDetails jwtUser = (UserDetails) authentication.getPrincipal();
        System.out.println("jwtUser=" + jwtUser);

        //获取角色信息， 并转为字符串列表
        List<String> roles = jwtUser.getAuthorities()
                .stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.toList());
        System.out.println("hhhhhhhhhh" + roles);

        System.out.println(jwtUser.isAccountNonLocked());
        System.out.println(jwtUser.isEnabled());
        System.out.println(jwtUser.isAccountNonExpired());
        System.out.println(jwtUser.isCredentialsNonExpired());
        System.out.println(jwtUser.getUsername());
        // 创建 Token
        String token = JwtUtil.createToken2(jwtUser.getUsername(), roles);
        // Http Response Header 中返回 Token
        try {
            response.setStatus(200);   //成功 HTTP code 200
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json");
            Map<String, String> map = new HashMap();
            map.put("token", token);
            response.getWriter().println(XResp.packJson(200, "登录成功", map));
        } catch (Exception e) {
            System.err.println("response 写失败");
        }
    }


    /****
     * 如果验证失败，返回未经授权
     *  unsuccessful  不成功    Authentication 你是谁， 登录验证
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException authenticationException) throws IOException {
        System.err.println("登录失败 ....");
        try {
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json");
            if (authenticationException.getClass().getSimpleName().equals("LockedException")) {
                /*账户被锁定，无法登录*/
                response.setStatus(555);
            }else {
                response.setStatus(401);   // 未授权
            }
            response.getWriter().println(XResp.packJson(401, "登录失败", authenticationException.getMessage()));
            response.flushBuffer();
        } catch (Exception e) {
            System.err.println("response 写失败");
        }
    }
}
